package ru.infosystem.server.dao.xml;

import java.io.File;
import java.io.IOException;
import java.text.*;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.SAXException;
import ru.infosystem.client.data.Student;
import ru.infosystem.server.dao.DaoStudent;
import org.w3c.dom.*;
import ru.infosystem.client.data.Group;
import ru.infosystem.client.data.ServerException;

public class XmlDaoStudent implements DaoStudent {
    
    Document doc;
    int autoID;
    
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, TransformerConfigurationException, TransformerException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document document = builder.parse("src/ru/infosystem/server/dao/xml/DataBase.xml");
        XmlDaoStudent xml = new XmlDaoStudent(document);
        
        SimpleDateFormat textFormat = new SimpleDateFormat("dd.MM.yyyy");
        Date date = null;
        // try {
        //    date = textFormat.parse("1812-11-04");
        // } catch (ParseException ex) {
        //     ex.printStackTrace();
        // }
        Student st = new Student(4, "Уткин", "Валерий", "Леонидович",
                6304, null);
        //xml.appendNewStudentNode(st);
        xml.deleteStudent(st);
        
        DOMSource dom_source = new DOMSource(xml.doc);
        
        StreamResult sr = new StreamResult(
                new File("src/ru/infosystem/server/dao/xml/DataBase.xml"));

        //new FileOutputStream("src/ru/infosystem/server/dao/xml/DataBase.xml"));
        Transformer t = TransformerFactory.newInstance().newTransformer();
        t.setOutputProperty(OutputKeys.METHOD, "xml");
        t.setOutputProperty(OutputKeys.INDENT, "yes");
        t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
        t.transform(dom_source, sr);
    }
    
    public XmlDaoStudent(Document doc) {
        this.doc = doc;
        
        NodeList nodeList = doc.getElementsByTagName("autoID");
        Node n = nodeList.item(0);
        this.autoID = Integer.valueOf(n.getTextContent());
        checkAutoID();
    }
    
    public int getAutoID() {
        autoID++;
        checkAutoID();
        NodeList nodeList = doc.getElementsByTagName("autoID");
        Node n = nodeList.item(0);
        n.setTextContent(String.valueOf(autoID));
        return autoID;
    }
    
    public void setAutoID(int autoID) {
        this.autoID = autoID;
        checkAutoID();
    }
    
    @Override
    public boolean deleteStudent(Student student) {
        //System.out.println(student.getSurname() + student.getName() + student.getPatronymic());
        boolean done = false;
        NodeList listOfStudents = this.doc.getElementsByTagName("student");
        List<Node> listOfNodesForDeleting = new ArrayList<>();
        if (listOfStudents != null) {
            for (int i = 0; i < listOfStudents.getLength(); i++) {
                NodeList studentData = listOfStudents.item(i).getChildNodes();
                if (studentData.item(1).getTextContent().equals(student.getName())
                        && studentData.item(3).getTextContent().equals(student.getSurname())
                        && studentData.item(5).getTextContent().equals(student.getPatronymic())) {
                    listOfNodesForDeleting.add(listOfStudents.item(i));
                    done = true;
                }
            }
        }
        if (done) {
            for (int i = 0; i < listOfNodesForDeleting.size(); i++) {
                listOfNodesForDeleting.get(i).getParentNode().removeChild(listOfNodesForDeleting.get(i));
            }
        }
        try {
            this.writeXml();
        } catch (TransformerConfigurationException ex) {
            Logger.getLogger(XmlDaoStudent.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerException ex) {
            Logger.getLogger(XmlDaoStudent.class.getName()).log(Level.SEVERE, null, ex);
        }
        return done;
    }
    
    @Override
    public Student getByID(int id) {
        Node node = findById(id, "student");
        String groupNumber = node.getParentNode().getAttributes().getNamedItem("number").getTextContent();
        return makeStudent(node, groupNumber);
    }
    
    @Override
    public boolean addStudent(Student student) throws ServerException{
        boolean done = this.appendNewStudentNode(student);
        try {
            this.writeXml();
        } catch (TransformerConfigurationException ex) {
            Logger.getLogger(XmlDaoStudent.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerException ex) {
            Logger.getLogger(XmlDaoStudent.class.getName()).log(Level.SEVERE, null, ex);
        }
        return done;
    }
    
    private void writeXml() throws TransformerConfigurationException, TransformerException {
        DOMSource source = new DOMSource(this.doc);
        
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
        StreamResult result = new StreamResult("src/ru/infosystem/server/dao/xml/DataBase.xml");
        transformer.transform(source, result);
        
        
        
        
    }
    
    private Node setStudentNode(Student student, Node studentNode) {
        
        for (Node childNode = studentNode.getFirstChild(); childNode != null;
                childNode = childNode.getNextSibling()) {
            switch (childNode.getNodeName()) {
                case "name":
                    childNode.setTextContent(student.getName());
                    break;
                case "surname":
                    childNode.setTextContent(student.getSurname());
                    break;
                case "patronymic":
                    childNode.setTextContent(student.getPatronymic());
                    break;
                case "acceptance":
                    //SimpleDateFormat textFormat = new SimpleDateFormat("yyyy-mm-dd");
                    //System.out.println("DAte format = " + );
                    childNode.setTextContent(student.getIncome().toString());
                    break;
                default:
                    ;
            }
        }
        NamedNodeMap attributes = studentNode.getAttributes();
        Attr IdAttribute = (Attr) attributes.getNamedItem("number");
        
        int ID = student.getID();
        if (ID == -1) {
            ID = getAutoID();
        }
        IdAttribute.setTextContent(String.valueOf(ID));
        return studentNode;
        
    }
    
    private Node findById(int id, String tagName) {
        NodeList nodeList = doc.getElementsByTagName(tagName);
        String sid = Integer.toString(id);
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node n = nodeList.item(i);
            NamedNodeMap attributes = n.getAttributes();
            if (sid.equals(attributes.getNamedItem("number").getTextContent())) {
                return n;
            }
        }
        return null;
    }
    
    private Element createEmptyStudentNode() {
        Element newStud = doc.createElement("student");
        
        newStud.setAttribute("number", "-1");
        
        String[] tagNames = {"name", "surname", "patronymic", "acceptance"};
        for (String tagName : tagNames) {
            Element txtTag = doc.createElement(tagName);
            txtTag.appendChild(doc.createTextNode(" "));
            newStud.appendChild(txtTag);
        }
        return newStud;
    }
    
    private boolean appendNewStudentNode(Student student) throws ServerException{
        Node group = findById(student.getGroupNumber(), "group");
        if (group == null) {
            ArrayList<Student> newStudent = new ArrayList<>();
            newStudent.add(student);
            // номер факультета берётся как первая цифра группы
            new XmlDaoGroup(this.doc).addNewGroup(new Group(student.getGroupNumber(), new Integer(student.getGroupNumber()).toString().substring(0, 1), newStudent));
            return true;
        }
        student.setID(this.getAutoID());
        Node studentNode = findById(student.getID(), "student");
        if (studentNode == null) {
            studentNode = createEmptyStudentNode();
            group.appendChild(studentNode);
        } else {
            throw new ServerException("Внутренняя ошика");
            //return false; // уже есть с таким айдишником, ошибка в программе
        }
        setStudentNode(student, studentNode);
        return true;
    }
    
    private void checkAutoID() {
        while (findById(autoID, "student") != null) {
            autoID++;
        }
        
    }
    
    interface ArrayFilter {
        
        boolean filter(Object o);
    }
    
    private void filterArray(List<Node> list, ArrayFilter af) {
        Iterator<Node> iterator = list.iterator();
        while (iterator.hasNext()) {
            Node node = iterator.next();
            if (!af.filter(node)) {
                iterator.remove();
            }
            iterator.next();
        }
    }
    
    private Student[] getStudentArray(List<Node> list) {
        Student[] result = new Student[list.size()];
        int i = 0;
        for (Node studentNode : list) {
            String groupNumber = studentNode.getParentNode().getAttributes().getNamedItem("number").getTextContent();
            result[i] = makeStudent(studentNode, groupNumber);
            i++;
        }
        return result;
    }
    
    private List<Node> getNodesBySubtagVal(String subTagName, String tagValue) {
        
        List<Node> listResult = new LinkedList<>();
        NodeList nodeList = doc.getElementsByTagName(subTagName);
        for (int i = 0; i < nodeList.getLength(); i++) {
            Node n = nodeList.item(i);
            if (tagValue.equals(n.getTextContent())) {
                listResult.add(n.getParentNode());
            }
        }
        return listResult;
    }
    
    @Override
    public Student[] getByFIO(String name, final String surname,
            final String patronymic) {
        List<Node> result = getNodesBySubtagVal("name", name);
        filterArray(result, new ArrayFilter() {
            @Override
            public boolean filter(Object obj) {
                Node student = (Node) obj;
                Node child = student.getFirstChild();
                while (child.getNodeName() != "surname") {
                    child = child.getNextSibling();
                }
                if (surname.equals(child.getTextContent())) {
                    return true;
                }
                return false;
            }
        });
        filterArray(result, new ArrayFilter() {
            @Override
            public boolean filter(Object obj) {
                Node student = (Node) obj;
                Node child = student.getFirstChild();
                while (child.getNodeName() != "patronymic") {
                    child = child.getNextSibling();
                }
                if (patronymic.equals(child.getTextContent())) {
                    return true;
                }
                return false;
            }
        });
        Student[] res = getStudentArray(result);
        return res;
    }
    
    private Student makeStudent(Node n, String groupNumber) {
        Student result = Student.NullStudent;
        String name = null, surname = null, patronymic = null, acceptance = null;
        for (Node childNode = n.getFirstChild(); childNode != null; childNode = childNode.getNextSibling()) {
            switch (childNode.getNodeName()) {
                case "name":
                    name = childNode.getTextContent();
                    break;
                case "surname":
                    surname = childNode.getTextContent();
                    break;
                case "patronymic":
                    patronymic = childNode.getTextContent();
                    break;
                case "acceptance":
                    acceptance = childNode.getTextContent();
                    break;
                default:
                    ;
            }
        }
        int ID;
        NamedNodeMap attributes = n.getAttributes();
        ID = Integer.parseInt(attributes.getNamedItem("number").getTextContent());
        
        SimpleDateFormat textFormat = new SimpleDateFormat("yyyy-mm-dd");
        Date date = null;
        try {
            date = textFormat.parse(acceptance);
        } catch (ParseException ex) {
            ex.printStackTrace();
        }
        result = new Student(ID, name, surname, patronymic,
                Integer.valueOf(groupNumber),
                new java.sql.Date(date.getTime()));
        return result;
    }
}
